package io.sundial.assembly;

import io.sundial.util.ResKit;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Stack;

/**
 * TAR格式
 *
 * @author Payne 646742615@qq.com
 * 2018/12/28 20:29
 */
public class TarAssembler implements Assembler {

    @Override
    public String suffix() {
        return "tar";
    }

    @Override
    public void assemble(File source, File target) throws IOException {
        FileOutputStream fileOutputStream = null;
        TarArchiveOutputStream tarArchiveOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(target);
            tarArchiveOutputStream = new TarArchiveOutputStream(fileOutputStream);
            Stack<String> parents = new Stack<>();
            Stack<File> files = new Stack<>();

            if (source.exists()) {
                parents.push(null);
                files.push(source);
            }

            while (!files.isEmpty()) {
                File file = files.pop();
                String parent = parents.pop();

                if (file.isDirectory()) {
                    TarArchiveEntry tarArchiveEntry = new TarArchiveEntry(parent == null ? file.getName() + "/" : parent + "/" + file.getName() + "/");
                    tarArchiveEntry.setModTime(file.lastModified());
                    tarArchiveOutputStream.putArchiveEntry(tarArchiveEntry);
                    tarArchiveOutputStream.closeArchiveEntry();
                    File[] children = file.listFiles();
                    for (int i = 0; children != null && i < children.length; i++) {
                        File child = children[i];
                        parents.push(parent == null ? file.getName() : parent + "/" + file.getName());
                        files.push(child);
                    }
                } else {
                    FileInputStream fileInputStream = null;
                    try {
                        fileInputStream = new FileInputStream(file);
                        TarArchiveEntry tarArchiveEntry = new TarArchiveEntry(file, parent == null ? file.getName() : parent + "/" + file.getName());
                        tarArchiveOutputStream.putArchiveEntry(tarArchiveEntry);
                        ResKit.transfer(fileInputStream, tarArchiveOutputStream);
                        tarArchiveOutputStream.closeArchiveEntry();
                    } finally {
                        ResKit.close(fileInputStream);
                    }
                }
            }
        } finally {
            ResKit.close(tarArchiveOutputStream);
            ResKit.close(fileOutputStream);
        }
    }

    @Override
    public void disassemble(File source, File target) throws IOException {
        FileInputStream fileInputStream = null;
        TarArchiveInputStream tarArchiveInputStream = null;
        try {
            fileInputStream = new FileInputStream(source);
            tarArchiveInputStream = new TarArchiveInputStream(fileInputStream);
            TarArchiveEntry tarArchiveEntry;
            while ((tarArchiveEntry = tarArchiveInputStream.getNextTarEntry()) != null) {
                if (tarArchiveEntry.isDirectory()) {
                    File parent = new File(target, tarArchiveEntry.getName());
                    if (!parent.exists() && !parent.mkdirs() && !parent.exists()) {
                        throw new IOException("could not make directory: " + parent);
                    }
                    continue;
                }
                FileOutputStream fileOutputStream = null;
                try {
                    File file = new File(target, tarArchiveEntry.getName());
                    File parent = file.getParentFile();
                    if (!parent.exists() && !parent.mkdirs() && !parent.exists()) {
                        throw new IOException("could not make directory: " + parent + " for tar archive entry: " + tarArchiveEntry);
                    }
                    fileOutputStream = new FileOutputStream(file);
                    ResKit.transfer(tarArchiveInputStream, fileOutputStream);
                } finally {
                    ResKit.close(fileOutputStream);
                }
            }
        } finally {
            ResKit.close(tarArchiveInputStream);
            ResKit.close(fileInputStream);
        }
    }
}
